package com.zwl.swagger.plugins;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.zwl.swagger.CommentContainer;
import com.zwl.swagger.SwaggerProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Enums;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.schema.ModelReference;
import springfox.documentation.service.AllowableListValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ExpandedParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterExpansionContext;

import java.util.Arrays;
import java.util.List;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.Lists.transform;
import static springfox.documentation.schema.Collections.collectionElementType;
import static springfox.documentation.schema.Collections.containerType;
import static springfox.documentation.schema.Collections.isContainerType;
import static springfox.documentation.schema.Types.typeNameFor;
import static springfox.documentation.service.Parameter.DEFAULT_PRECEDENCE;

/**
 *  针对表单参数的属性展开，但是对body参数无效
 * @author zhangweilin
 * @date 2021/10/28 10:07
 * @Description:
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class ExpandedParameterBuilderPlus implements ExpandedParameterBuilderPlugin, ContextDescriper {
    private final TypeResolver resolver;
    private final EnumTypeDeterminer enumTypeDeterminer;

    @Autowired
    private CommentContainer commentContainer;

	@Autowired
	private SwaggerProperties swaggerProperties;

    @Autowired
    public ExpandedParameterBuilderPlus(
            TypeResolver resolver,
            EnumTypeDeterminer enumTypeDeterminer) {
        this.resolver = resolver;
        this.enumTypeDeterminer = enumTypeDeterminer;
    }

    @Override
    public void apply(ParameterExpansionContext context) {
        try {
            AllowableValues allowable = allowableValues(context.getFieldType().getErasedType());

            String name = isNullOrEmpty(context.getParentName())
                    ? context.getFieldName()
                    : String.format("%s.%s", context.getParentName(), context.getFieldName());
//			System.out.println("ExpandedParameterBuilderPlus.name = " + name);
			boolean isHidden = false;
			List<String> auditFieldList = swaggerProperties.getCore().getAuditField();
			if (auditFieldList != null && !auditFieldList.isEmpty()) {
				for (String auditField : auditFieldList) {
					if (name.endsWith("." + auditField) || name.equals(auditField)) {
//						审计字段隐藏，没必要出现在参数中
						isHidden = true;
						break;
					}
				}
			}

			String typeName = context.getDataTypeName();
			ModelReference itemModel = null;
            ResolvedType resolved = resolver.resolve(context.getFieldType());
            if (isContainerType(resolved)) {
                resolved = fieldType(context).or(resolved);
                ResolvedType elementType = collectionElementType(resolved);
                String itemTypeName = typeNameFor(elementType.getErasedType());
                AllowableValues itemAllowables = null;
                if (enumTypeDeterminer.isEnum(elementType.getErasedType())) {
                    itemAllowables = Enums.allowableValues(elementType.getErasedType());
                    itemTypeName = "string";
                }
                typeName = containerType(resolved);
                itemModel = new ModelRef(itemTypeName, itemAllowables);
            } else if (enumTypeDeterminer.isEnum(resolved.getErasedType())) {
                typeName = "string";
            }

            String fieldsDescription = getFieldsDescription(context);
            String fieldsComment = commentContainer.getFieldCommentMap().get(fieldsDescription);
            Boolean required = isRequired(fieldsComment);

            context.getParameterBuilder()
                    .name(name)
					.hidden(isHidden)
                    .description(fieldsComment)
                    .defaultValue(null)
                    .required(required)
                    .allowMultiple(isContainerType(resolved))
                    .type(resolved)
                    .modelRef(new ModelRef(typeName, itemModel))
                    .allowableValues(allowable)
                    .parameterType(context.getParameterType())
                    .order(DEFAULT_PRECEDENCE)
                    .parameterAccess(null);
        } catch (Exception e) {
			log.error("ExpandedParameterBuilderPlus异常，context: " + context,e);
        }
    }



    private Optional<ResolvedType> fieldType(ParameterExpansionContext context) {
        return Optional.of(context.getFieldType());
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }

    private AllowableValues allowableValues(Class<?> fieldType) {

        AllowableValues allowable = null;
        if (enumTypeDeterminer.isEnum(fieldType)) {
            allowable = new AllowableListValues(getEnumValues(fieldType), "LIST");
        }

        return allowable;
    }

    private List<String> getEnumValues(final Class<?> subject) {
        return transform(Arrays.asList(subject.getEnumConstants()), new Function<Object, String>() {
            @Override
            public String apply(final Object input) {
                return input.toString();
            }
        });
    }
}
